﻿
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Threading;
using Xunit;
using Xunit.Extensions;

namespace EmperialApps.WeatherSpark.Data {

    public class TestCoordinate {

        [Fact]
        public void ctor_GivenDoubleValue_ReturnsRoundedValue( ) {
            var expected = Tuple.Create( 12.35, 67.89 );

            var coordinate = new Coordinate( 12.3456, 67.8901 );
            var actual = Tuple.Create( coordinate.Latitude, coordinate.Longitude );

            Assert.Equal( expected, actual );
        }


        [Theory]
        [PropertyData( Coordinates_Name )]
        public void ToString_ReturnsExpectedValue( string latitudeString, string longitudeString, bool display, CultureInfo culture ) {
            string expectedLatitude = GetExpectedString( latitudeString, display, culture );
            string expectedLongitude = GetExpectedString( longitudeString, display, culture );
            double latitude = double.Parse( latitudeString );
            double longitude = double.Parse( longitudeString );
            var coordinate = new Coordinate( latitude, longitude );

            string actual = Test( s => s.Item1.ToString( s.Item2 ), Pair.Create( coordinate, display ), culture );

            Assert.Contains( expectedLatitude, actual );
            Assert.Contains( expectedLongitude, actual );
            if( display ) {
                Assert.Contains( culture.TextInfo.ListSeparator, actual );
                Assert.DoesNotContain( culture.TextInfo.ListSeparator, expectedLatitude + expectedLongitude );
            }
            else {
                Assert.DoesNotContain( " ", actual );
            }
        }


        [Theory]
        [PropertyData( Coordinates_Name )]
        public void Parse_GivenToStringValue_ReturnsOriginalCoordinate( string latitudeString, string longitudeString, bool display, CultureInfo culture ) {
            double latitude = double.Parse( latitudeString );
            double longitude = double.Parse( longitudeString );
            var expected = new Coordinate( latitude, longitude );
            string input = expected.ToString( display );

            var actual = Test( Coordinate.Parse, input, culture );

            Assert.Equal( expected, actual );
        }

        [Theory]
        [PropertyData( CoordinateStrings_Name )]
        public void Parse_GivenInvariantStringValue_ReturnsOriginalCoordinate( string latitudeString, string longitudeString ) {
            double latitude = double.Parse( latitudeString );
            double longitude = double.Parse( longitudeString );
            var expected = new Coordinate( latitude, longitude );
            string input = latitude + ", " + longitude;

            var actual = Test( Coordinate.Parse, input, German );

            Assert.Equal( expected, actual );
        }


        #region Utility

        private static readonly CultureInfo German = new CultureInfo( "de-DE" );

        private const string CoordinateStrings_Name = "CoordinateStrings";
        public static IEnumerable<object[]> CoordinateStrings {
            get {
                yield return new object[] { "12.34", "-56.78" };
                yield return new object[] { "0.03", "53.73" };
                yield return new object[] { "3", "153.7" };
                yield return new object[] { "-89.76", "0.4" };
            }
        }

        private const string Coordinates_Name = "Coordinates";
        public static IEnumerable<object[]> Coordinates {
            get {
                foreach( var culture in new[] { CultureInfo.InvariantCulture, German } )
                    foreach( bool display in new[] { true, false } )
                        foreach( object[] strings in CoordinateStrings )
                            yield return new[] { strings[0], strings[1], display, culture };
            }
        }


        private static string GetExpectedString( string coordinateString, bool display, CultureInfo culture ) {
            return display
                 ? coordinateString.Replace( ".", culture.NumberFormat.NumberDecimalSeparator )
                 : coordinateString.Replace( ".", "" ).TrimStart( '0' );
        }

        private static TResult Test<TState, TResult>( Func<TState, TResult> test, TState state, CultureInfo culture ) {
            var thread = Thread.CurrentThread;
            var currentCulture = thread.CurrentCulture;
            try {
                thread.CurrentCulture = culture;
                return test( state );
            }
            finally {
                thread.CurrentCulture = currentCulture;
            }
        }

        #endregion

    }

}
